// =====================================================================
//
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================
#region

using System;
using System.Diagnostics;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Discovery;
using SL.IdentityModel.Protocols.WSTrust;


#endregion

namespace Microsoft.Xrm.Sdk.Client
{
	public sealed class OrganizationServiceConfiguration : IServiceConfiguration<IOrganizationService>, IServiceManagement<IOrganizationService>
	{

		#region IServiceConfiguration<IOrganizationService> Members

		public ServiceEndpoint CurrentServiceEndpoint { get { return service.CurrentServiceEndpoint; } set { service.CurrentServiceEndpoint = value; } }

		public IssuerEndpoint CurrentIssuer { get { return service.CurrentIssuer; } set { service.CurrentIssuer = value; } }

		public AuthenticationProviderType AuthenticationType { get { return service.AuthenticationType; } }

		public Microsoft.Xrm.Sdk.Client.ServiceEndpointMetadata.ServiceEndpointDictionary ServiceEndpoints { get { return service.ServiceEndpoints; } }

		public IssuerEndpointDictionary IssuerEndpoints { get { return service.IssuerEndpoints; } }

		public CrossRealmIssuerEndpointCollection CrossRealmIssuerEndpoints { get { return service.CrossRealmIssuerEndpoints; } }

		public PolicyConfiguration PolicyConfiguration { get { return service.PolicyConfiguration; } }


		public void Authenticate(AuthenticationCredentials authenticationCredentials)
		{
			service.Authenticate(authenticationCredentials);
		}
		#endregion




		private const string XrmServicesRoot = "xrmservices/";
		private readonly ServiceConfiguration<IOrganizationService> service;
		private object _lockObject = new object();

		private OrganizationServiceConfiguration()
		{
		}

		internal OrganizationServiceConfiguration(Uri serviceUri)
			: this(serviceUri, null)
		{
		}

		internal OrganizationServiceConfiguration(Uri serviceUri,
												  EventHandler<ServiceMetadataLoadedEventArgs> metadataLoaded)
		{
			service = new ServiceConfiguration<IOrganizationService>(serviceUri, ServiceMetadataLoaded);
			service.DeviceSignInComplete += ServiceDeviceSignInComplete;
			service.SignInComplete += ServiceSignInComplete;
			service.MetadataLoadFailure += ServiceMetadataLoadFailure;

			service.AuthenticationComplete += OnInnerServiceAuthenticationComplete;

			this.MetadataLoaded += metadataLoaded;
		}

		#region IServiceConfiguration<IOrganizationService> Members


		public Uri ServiceUri
		{
			get { return service.ServiceUri; }
		}

		public ServiceEndpointMetadata ServiceEndpointMetadata
		{
			get { return service.ServiceEndpointMetadata; }
		}

		public void Authenticate(ClientCredentials clientCredentials, Uri uri, string keyType)
		{
			service.Authenticate(clientCredentials, uri, keyType);
		}

		public void Authenticate(RequestSecurityTokenResponse securityToken, Uri uri, string keyType)
		{
			service.Authenticate(securityToken, uri, keyType);
		}

		public void Authenticate(ClientCredentials clientCredentials)
		{
			service.Authenticate(clientCredentials);
		}

		public void Authenticate(RequestSecurityTokenResponse securityTokenResponse)
		{
			service.Authenticate(securityTokenResponse);
		}

		public void AuthenticateCrossRealm(ClientCredentials clientCredentials, String appliesTo, Uri crossRealmSts)
		{
			service.AuthenticateCrossRealm(clientCredentials, appliesTo, crossRealmSts);
		}


		public void AuthenticateCrossRealm(RequestSecurityTokenResponse securityTokenResponse, String appliesTo, Uri crossRealmSts)
		{
			service.AuthenticateCrossRealm(securityTokenResponse, appliesTo, crossRealmSts);
		}


		public void Authenticate(ClientCredentials clientCredentials, RequestSecurityTokenResponse deviceSecurityToken)
		{
			service.Authenticate(clientCredentials, deviceSecurityToken);
		}


		public void AuthenticateDevice(ClientCredentials clientCredentials)
		{
			service.AuthenticateDevice(clientCredentials);
		}

		public event EventHandler<AuthenticationCompletedEventArgs> AuthenticationComplete;
		public event EventHandler<SignOnCompletedEventArgs> SignInComplete;
		public event EventHandler<ServiceMetadataLoadedEventArgs> MetadataLoaded;
		public event EventHandler<SignOnCompletedEventArgs> DeviceSignInComplete;

		public event EventHandler<ServiceMetadataLoadedEventArgs> MetadataLoadFailure;
		public event EventHandler<IdentityProvidersLoadCompletedEventArgs> IdentityProviderLoaded;

		#endregion

		private void OnMetadataLoaded(ServiceMetadataLoadedEventArgs args)
		{
			if (MetadataLoaded != null)
			{
				MetadataLoaded(this, args);
			}
		}

		private void ServiceMetadataLoaded(object sender, ServiceMetadataLoadedEventArgs e)
		{
			OnMetadataLoaded(e);
		}

		private void OnSignInCompleted(SignOnCompletedEventArgs args)
		{
			if (SignInComplete != null)
			{
				SignInComplete(this, args);
			}
		}

		private void ServiceSignInComplete(object sender, SignOnCompletedEventArgs e)
		{
			OnSignInCompleted(e);
		}

		private void OnDeviceSignInCompleted(SignOnCompletedEventArgs args)
		{
			if (DeviceSignInComplete != null)
			{
				DeviceSignInComplete(this, args);
			}
		}

		private void ServiceDeviceSignInComplete(object sender, SignOnCompletedEventArgs e)
		{
			OnDeviceSignInCompleted(e);
		}

		private void OnMetadataLoadFailure(ServiceMetadataLoadedEventArgs args)
		{
			if (MetadataLoadFailure != null)
			{
				MetadataLoadFailure(this, args);
			}
		}

		private void ServiceMetadataLoadFailure(object sender, ServiceMetadataLoadedEventArgs e)
		{
			OnMetadataLoadFailure(e);
		}

		private void OnInnerServiceAuthenticationComplete(object sender, AuthenticationCompletedEventArgs e)
		{
			if (AuthenticationComplete != null)
			{
				AuthenticationComplete(this, e);
			}
		}

		public void GetIdentityProvider(String userPrincipalName)
		{
			service.IdentityProviderLoaded += new EventHandler<IdentityProvidersLoadCompletedEventArgs>(innerService_IdentityProviderLoaded);
			service.GetIdentityProvider(userPrincipalName);
		}

		void innerService_IdentityProviderLoaded(object sender, IdentityProvidersLoadCompletedEventArgs e)
		{
			this.IdentityProviderLoaded(this, e);
		}
	}

	internal sealed class DiscoveryServiceConfiguration : IServiceConfiguration<IDiscoveryService>, IServiceManagement<IDiscoveryService>
	{
		public void Authenticate(AuthenticationCredentials authenticationCredentials)
		{
			service.Authenticate(authenticationCredentials);
		}

		public void GetIdentityProvider(String userPrincipalName)
		{
			service.IdentityProviderLoaded += new EventHandler<IdentityProvidersLoadCompletedEventArgs>(innerService_IdentityProviderLoaded);
			service.GetIdentityProvider(userPrincipalName);
		}

		void innerService_IdentityProviderLoaded(object sender, IdentityProvidersLoadCompletedEventArgs e)
		{
			service.IdentityProviderLoaded -= new EventHandler<IdentityProvidersLoadCompletedEventArgs>(innerService_IdentityProviderLoaded);
			this.IdentityProviderLoaded(this, e);
		}

		#region IServiceConfiguration<IDiscoveryService> Members

		public ServiceEndpoint CurrentServiceEndpoint { get { return service.CurrentServiceEndpoint; } set { service.CurrentServiceEndpoint = value; } }

		public IssuerEndpoint CurrentIssuer { get { return service.CurrentIssuer; } set { service.CurrentIssuer = value; } }

		public AuthenticationProviderType AuthenticationType { get { return service.AuthenticationType; } }

		public Microsoft.Xrm.Sdk.Client.ServiceEndpointMetadata.ServiceEndpointDictionary ServiceEndpoints { get { return service.ServiceEndpoints; } }

		public IssuerEndpointDictionary IssuerEndpoints { get { return service.IssuerEndpoints; } }

		public CrossRealmIssuerEndpointCollection CrossRealmIssuerEndpoints { get { return service.CrossRealmIssuerEndpoints; } }

		public PolicyConfiguration PolicyConfiguration { get { return service.PolicyConfiguration; } }

		#endregion




		private readonly ServiceConfiguration<IDiscoveryService> service;

		private DiscoveryServiceConfiguration()
		{
		}

		internal DiscoveryServiceConfiguration(Uri serviceUri)
			: this(serviceUri, null)
		{
		}

		public DiscoveryServiceConfiguration(Uri serviceUri, EventHandler<ServiceMetadataLoadedEventArgs> metadataLoaded)
		{
			service = new ServiceConfiguration<IDiscoveryService>(serviceUri, ServiceMetadataLoaded);
			service.DeviceSignInComplete += ServiceDeviceSignInComplete;
			service.SignInComplete += ServiceSignInComplete;
			service.MetadataLoadFailure += ServiceMetadataLoadFailure;

			service.AuthenticationComplete += OnInnerServiceAuthenticationComplete;

			this.MetadataLoaded += metadataLoaded;
		}

		#region IServiceConfiguration<IDiscoveryService> Members

		public Uri ServiceUri
		{
			get { return service.ServiceUri; }
		}

		public ServiceEndpointMetadata ServiceEndpointMetadata
		{
			get { return service.ServiceEndpointMetadata; }
		}

		public void Authenticate(ClientCredentials clientCredentials)
		{
			service.Authenticate(clientCredentials);
		}

		public void Authenticate(RequestSecurityTokenResponse securityToken)
		{
			service.Authenticate(securityToken);
		}

		public void AuthenticateCrossRealm(ClientCredentials clientCredentials, String appliesTo, Uri crossRealmSts)
		{
			service.AuthenticateCrossRealm(clientCredentials, appliesTo, crossRealmSts);
		}

		public void AuthenticateCrossRealm(RequestSecurityTokenResponse securityTokenResponse, String appliesTo, Uri crossRealmSts)
		{
			service.AuthenticateCrossRealm(securityTokenResponse, appliesTo, crossRealmSts);
		}

		public void Authenticate(ClientCredentials clientCredentials, RequestSecurityTokenResponse deviceSecurityToken)
		{
			service.Authenticate(clientCredentials, deviceSecurityToken);
		}


		public void AuthenticateDevice(ClientCredentials clientCredentials)
		{
			service.AuthenticateDevice(clientCredentials);
		}

		public event EventHandler<AuthenticationCompletedEventArgs> AuthenticationComplete;
		public event EventHandler<SignOnCompletedEventArgs> SignInComplete;
		public event EventHandler<ServiceMetadataLoadedEventArgs> MetadataLoaded;
		public event EventHandler<SignOnCompletedEventArgs> DeviceSignInComplete;
		public event EventHandler<ServiceMetadataLoadedEventArgs> MetadataLoadFailure;
		public event EventHandler<IdentityProvidersLoadCompletedEventArgs> IdentityProviderLoaded;
		#endregion

		private void OnMetadataLoaded(ServiceMetadataLoadedEventArgs args)
		{
			Debug.WriteLine("DiscoveryServiceInformation.MetadataLoaded");
			if (MetadataLoaded != null)
			{
				MetadataLoaded(this, args);
			}
		}

		private void OnInnerServiceAuthenticationComplete(object sender, AuthenticationCompletedEventArgs e)
		{
			if (AuthenticationComplete != null)
			{
				AuthenticationComplete(this, e);
			}
		}

		private void ServiceMetadataLoaded(object sender, ServiceMetadataLoadedEventArgs e)
		{
			OnMetadataLoaded(e);
		}

		private void OnMetadataLoadFailure(ServiceMetadataLoadedEventArgs args)
		{
			if (MetadataLoadFailure != null)
			{
				MetadataLoadFailure(this, args);
			}
		}

		private void ServiceMetadataLoadFailure(object sender, ServiceMetadataLoadedEventArgs e)
		{
			OnMetadataLoadFailure(e);
		}

		private void OnSignInCompleted(SignOnCompletedEventArgs args)
		{
			if (SignInComplete != null)
			{
				SignInComplete(this, args);
			}
		}

		private void ServiceSignInComplete(object sender, SignOnCompletedEventArgs e)
		{
			OnSignInCompleted(e);
		}

		private void OnDeviceSignInCompleted(SignOnCompletedEventArgs args)
		{
			if (DeviceSignInComplete != null)
			{
				DeviceSignInComplete(this, args);
			}
		}

		private void ServiceDeviceSignInComplete(object sender, SignOnCompletedEventArgs e)
		{
			OnDeviceSignInCompleted(e);
		}
	}

	public static class ServiceConfigurationFactory
	{
		public static IServiceConfiguration<TService> CreateConfiguration<TService>(Uri serviceUri,
																					EventHandler
																						<ServiceMetadataLoadedEventArgs>
																						metadataLoaded)
		{
			if (serviceUri != null)
			{
				if (typeof(TService) == typeof(IDiscoveryService))
				{
					return
						new DiscoveryServiceConfiguration(serviceUri, metadataLoaded) as IServiceConfiguration<TService>;
				}
				else if (typeof(TService) == typeof(IOrganizationService))
				{
					return
						new OrganizationServiceConfiguration(serviceUri, metadataLoaded) as
						IServiceConfiguration<TService>;
				}
			}
			return null;
		}



		public static IServiceManagement<TService> CreateManagement<TService>(Uri serviceUri,
			EventHandler<ServiceMetadataLoadedEventArgs> metadataLoaded)
		{
			if (serviceUri != null)
			{
				if (typeof(TService) == typeof(IDiscoveryService))
				{
					return new DiscoveryServiceConfiguration(serviceUri, metadataLoaded) as IServiceManagement<TService>;
				}
				else if (typeof(TService) == typeof(IOrganizationService))
				{
					return new OrganizationServiceConfiguration(serviceUri, metadataLoaded) as IServiceManagement<TService>;
				}
			}
			return null;
		}

	}
}
